package com.bup.gn.utils.exceptions.handler;

import com.bup.gn.utils.exceptions.ExceptionInfo;
import com.bup.gn.utils.xml.XmlParser;
import com.bup.gn.utils.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.util.HashMap;

/**
 * This is a singleton which acts as a cache for <code>ExceptionInfoDTO</code>
 * objects.
 * <p/>
 * <p/>
 * <b>Overview: </b>
 * <p/>
 * It reads a file called <code>exceptionInfo.xml</code> which contains a
 * mapping of the exception type with message key and context indicator. It
 * parses the <code>exceptionInfo.xml</code> file and creates
 * <code>ExceptionInfoDTO</code> objects out of that. The sample XML file will
 * be something like this.
 * <p/>
 * <pre>
 *  &lt;?xml	version='1.0'?&gt;
 *  &lt;exceptioninfo&gt;
 *  &lt;exception name=&quot;UniqueConstraintException&quot;&gt;
 *  &lt;messagecode&gt;errorcode.uniqueconstraint&lt;/message&gt;
 *  &lt;contextind&gt;true&lt;/contextindi&gt;
 *  &lt;/exception&gt;
 *  &lt;exception name=&quot;XmlParseException&quot;&gt;
 *  &lt;messagecode&gt;errorcode.xmlparse&lt;/message&gt;
 *  &lt;contextind&gt;false&lt;/contextindi&gt;
 *  &lt;/exception&gt;
 *  &lt;/exceptionInfo&gt;
 * </pre>
 * <p/>
 * Above exceptioninfo.xml is based on following DTD
 * <p/>
 * <pre>
 *  &lt;!ELEMENT exceptioninfo(exception+) &gt;
 *  &lt;!ELEMENT exception(messagecode, contextind?, confirmationind ?, loggingtype? &gt;
 *  &lt;!ATTLIST exception name CDATA #REQUIRED&gt;
 *  &lt;!ELEMENT messagecode (#PCDATA)&gt;
 *  &lt;!ELEMENT contextind (#PCDATA)&gt;
 *  &lt;!ELEMENT confirmationind (#PCDATA)&gt;
 *  &lt;!ELEMENT loggingtype (#PCDATA)&gt;
 * </pre>
 * <p/>
 * In above DTD, contextind, confirmationind and loggingtype tags are
 * optional.While creating ExceptionInfoDTO objects, following values are taken
 * as default for these tags if not specified in the XML.
 * <p/>
 * <pre>
 *  contextind ---&gt; false
 *  confirmationind --&gt; false
 *  loggingtype --&gt; error (other possible values are &quot;warning&quot;, &quot;info&quot; and &quot;nologging&quot; if specified).
 * </pre>
 * <p/>
 * It's mandatory to specify "nologging" as logging type for confirmation
 * exception (confirmationind = true) in <code>exceptioninfo.xml</code>.
 *
 * @author ShriKant
 * @version 1.0
 */
public class ExceptionInfoCache {

    private static final ExceptionInfoCache SINGLETON = new ExceptionInfoCache();

    private HashMap ecache = new HashMap();

    /**
     * It's a private constructor for singleton instance.
     */
    private ExceptionInfoCache() {

        try {
            Document doc = XmlParser.parse("/expinfo.xml");
            Element[] elements = XmlUtil.getElements("exception", doc);
            if (elements != null && elements.length > 0) {
                for (Element exElement : elements) {
                    addException(exElement);
                }
            }
        } catch (Throwable t) {
            ecache = null;
        }
    }

    private void addException(Element exElement) {
        String exName = exElement.getAttribute("name");
        String context = XmlUtil.getTagValue(exElement, "contextind");
        String messageCode = XmlUtil.getTagValue(exElement, "messagecode");
        boolean confirmationInd = false;
        String confirmation = XmlUtil.getTagValue(exElement, "confirmationind");
        if (confirmation != null)
            confirmationInd = Boolean.valueOf(confirmation);
        String loggingType = XmlUtil.getTagValue(exElement, "loggingtype");
        ExceptionInfo exInfo = new ExceptionInfo(messageCode, confirmationInd, loggingType);
        ecache.put(exName, exInfo);
    }

    /**
     * Gets the singleton instance of <code>ExceptionInfoCache</code> class.
     *
     * @return <code>ExceptionInfoCache</code> instance
     */
    public static ExceptionInfoCache getInstance() {
        return SINGLETON;
    }

    /**
     * Based on exception type passed it returns the
     * <code>ExceptionInfoDTO</code> object from cache.
     *
     * @param errorId error id String.
     * @return <code>ExceptionInfoDTO</code>
     */
    public ExceptionInfo getExceptionInfo(String errorId) {
        if (ecache == null) {
            return null;
        }
        return (ExceptionInfo) ecache.get(errorId);
    }
}